package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.convert.*;
import cn.mw.cmdb.enums.RecordLogErrorMethodType;
import cn.mw.cmdb.exception.NoModelServiceException;
import cn.mw.cmdb.listener.InstanceChangeEventLister;
import cn.mw.cmdb.mongoMapper.*;
import cn.mw.cmdb.processor.BusinessOperationService;
import cn.mw.cmdb.service.*;
import cn.mw.cmdb.service.interceptors.ServiceInterceptor;
import cn.mw.cmdb.task.AssetsDiscoveryRunManage;
import cn.mw.cmdb.view.ViewManager;
import cn.mw.main.api.common.ZabbixDubboService;
import cn.mw.microMonitorCommon.api.alert.AlertApiService;
import cn.mw.microMonitorCommon.api.common.MwEngineManageService;
import cn.mw.microMonitorCommon.api.common.MwSyncEngineCommonService;
import cn.mw.microMonitorCommon.api.common.MwTPServerService;
import cn.mw.microMonitorCommon.api.powerjob.PowerJobService;
import cn.mw.microMonitorCommon.api.scan.AssetScanService;
import cn.mw.microMonitorCommon.api.user.MwUserApi;
import cn.mw.microMonitorCommon.util.ModuleIDManager;
import cn.mw.microMonitorCommon.util.RedisUtils;
import cn.mw.scanengine.service.ScanEngineService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class CmdbServiceManage implements InitializingBean, ApplicationContextAware {

    public static final String serviceId = "serviceId";

    private Map<Class, ServiceParamConvert> serviceParamConvertMap = new HashMap();

    private Map<String, ModelService> modelServiceMap = new HashMap<>();

    private ApplicationContext applicationContext;

    private ModuleIDManager moduleIDManager;

    @DubboReference(check = false, mock = "true" ,timeout = 15000)
    private AssetScanService assetScanService;
    @DubboReference(check = false, mock = "true", timeout = 15000)
    private MwTPServerService mwTPServerService;

    @DubboReference(check = false, mock = "true", timeout = 15000)
    private MwUserApi mwUserApi;
    @DubboReference(check = false, mock = "true", timeout = 15000)
    private AlertApiService alertApiService;
    @DubboReference(check = false, timeout = 15000)
    private PowerJobService powerJobService;

    @DubboReference(check = false, mock = "true", timeout = 15000)
    private MwEngineManageService mwEngineManageService;

    @DubboReference(check = false, timeout = 120000)
    private ZabbixDubboService zabbixDubboService;

    @Autowired
    private GridFsTemplate gridFsTemplate;

    @Autowired
    private List<ServiceInterceptor> interceptorList;
    @DubboReference(check = false, mock = "true" ,timeout = 15000)
    private MwSyncEngineCommonService mwSyncEngineCommonService;

    @Autowired
    private List<InstanceChangeEventLister> listerList;

    @Autowired
    private TableHeaderAndDataManage tableHeaderAndDataManage;

    @Autowired
    private TableViewManageService tableViewManageService;

    @Autowired
    private MessageSource messageSource;

    @Autowired
    private InstanceImportHistoryService instanceImportHistoryService;

    public CmdbServiceManage(ModuleIDManager moduleIDManager) {
        this.moduleIDManager = moduleIDManager;
    }

    public ModelService getModelService(String id) {
        return modelServiceMap.get(id);
    }

    public Object service(Object param, ServiceAction action) throws Exception {
        ModelService modelService = chooseModelService(param);

        Object destObj = null;
        if (param instanceof ServiceChoose) {
            destObj = param;
        } else {
            destObj = convert(modelService, (Map) param);
        }

        Object ret = null;
        //拦截判断处理
        for (ServiceInterceptor interceptor : interceptorList) {
            boolean intercept = interceptor.intercept(destObj, action);
            if (intercept) {
                return ret;
            }
        }
        switch (action) {
            case add -> ret = modelService.add(destObj);
            case batchAdd -> ret = modelService.batchAdd(destObj);
            case update -> ret = modelService.update(destObj);
            case batchUpd -> ret = modelService.batchUpdate(destObj);
            case remove -> ret = modelService.remove(destObj);
            case batchRemove -> ret = modelService.batchRemove(destObj);
            case select -> ret = modelService.selectById(destObj);
            case list -> ret = modelService.selectList(destObj);
        }

        BusinessOperationService businessOperationService = applicationContext.getBean(BusinessOperationService.class);
        businessOperationService.executeBusinessInstanceOperation(destObj, ret, action);

        return ret;
    }

    //批量新增
    public boolean batchAdd(Map param) {
        return false;
    }

    //批量更新
    public boolean batchUpdate(Map param) {
        return false;
    }

    //批量删除
    public boolean batchRemove(Map param) {
        return false;
    }

    private ModelService chooseModelService(Object param) {
        //查找对应模型服务
        String key = null;
        if (param instanceof ServiceChoose) {
            key = ((ServiceChoose) param).getServiceId();
        } else {
            key = ((Map) param).get(CmdbServiceManage.serviceId).toString();
        }
        if (!StringUtils.hasText(key)) {
            throw new NoModelServiceException();
        }

        ModelService modelService = modelServiceMap.get(key);
        if (null == modelService) {
            throw new NoModelServiceException();
        }

        return modelService;
    }

    //对参数进行转换
    public Object convert(ModelService modelService, Map param) throws Exception {
        Object destObj = modelService.genObject();
        ServiceParamConvert serviceParamConvert = serviceParamConvertMap.get(modelService.getClass());
        serviceParamConvert.convert(param, destObj);
        return destObj;
    }

    /**
     * 操作日志异常记录
     *
     * @param param
     * @param stackTraceElement
     */
    public static void operateLogRecordErrorMethod(Object param, StackTraceElement stackTraceElement) {
        if (stackTraceElement != null) {
            String methodName = stackTraceElement.getMethodName();
            String className = stackTraceElement.getClassName();
            String logMethodName = RecordLogErrorMethodType.getMethodName(methodName);
            try {
                Class<?> clazz = Class.forName(className);
                Object instanceObj = clazz.getDeclaredConstructor().newInstance();;
                Method method = clazz.getMethod(logMethodName, Object.class);
                method.invoke(instanceObj, param);
            } catch (Exception e) {
               log.error("fail to operateLogRecordErrorMethod::",e);
            }
        }

    }


    @Override
    public void afterPropertiesSet() throws Exception {
        ModelManage modelManage = applicationContext.getBean(ModelManage.class);
        ModelInfoMapper custModelMapper = applicationContext.getBean(ModelInfoMapper.class);
        ModelVendorMapper modelVendorMapper = applicationContext.getBean(ModelVendorMapper.class);
        InstanceInfoMapper instanceInfoMapper = applicationContext.getBean(InstanceInfoMapper.class);
        UNumManageMapper uNumManageMapper = applicationContext.getBean(UNumManageMapper.class);
        ModelGroupInfoMapper modelGroupInfoMapper = applicationContext.getBean(ModelGroupInfoMapper.class);
        ModelSpecificationMapper modelSpecificationMapper = applicationContext.getBean(ModelSpecificationMapper.class);
        ModelTemplateMapper modelTemplateMapper = applicationContext.getBean(ModelTemplateMapper.class);
        InstanceMonitorMapper instanceMonitorMapper = applicationContext.getBean(InstanceMonitorMapper.class);
        ModelMacOuiMapper modelMacOuiMapper = applicationContext.getBean(ModelMacOuiMapper.class);
        ModelPropertyCatologMapper modelPropertyCatologMapper = applicationContext.getBean(ModelPropertyCatologMapper.class);
        InstanceDetailTabMapper instanceDetailTabMapper = applicationContext.getBean(InstanceDetailTabMapper.class);
        LayoutItemDataMapper layoutItemDataMapper = applicationContext.getBean(LayoutItemDataMapper.class);
        AssetsScanResultMapper assetsScanResultMapper = applicationContext.getBean(AssetsScanResultMapper.class);
        TableHeaderMapper tableHeaderMapper = applicationContext.getBean(TableHeaderMapper.class);
        ItemNameMapper itemNameMapper = applicationContext.getBean(ItemNameMapper.class);
        CustomClassificationMapper customClassificationMapper = applicationContext.getBean(CustomClassificationMapper.class);
//        PropertyTypeChangeManage propertyTypeChangeManage = applicationContext.getBean(PropertyTypeChangeManage.class);
        TableSearchConditionMapper tableSearchConditionMapper = applicationContext.getBean(TableSearchConditionMapper.class);
        AssetsDiscoverMapper assetsDiscoverMapper = applicationContext.getBean(AssetsDiscoverMapper.class);
        MwCustomRuleMapper mwCustomRuleMapper = applicationContext.getBean(MwCustomRuleMapper.class);
        AlgorithmInfoMapper algorithmInfoMapper = applicationContext.getBean(AlgorithmInfoMapper.class);
        InstanceMetricInfoMapper instanceMetricInfoMapper = applicationContext.getBean(InstanceMetricInfoMapper.class);
        ModelMacroMapper modelMacroMapper = applicationContext.getBean(ModelMacroMapper.class);
        MwCMDBConfigMapper mwCMDBConfigMapper = applicationContext.getBean(MwCMDBConfigMapper.class);
        AssetsCodeCacheMapper assetsCodeCacheMapper = applicationContext.getBean(AssetsCodeCacheMapper.class);
        ViewManager viewManager = applicationContext.getBean(ViewManager.class);
        InventoryWorkInfoMapper inventoryWorkInfoMapper = applicationContext.getBean(InventoryWorkInfoMapper.class);
        InventoryWorkAssetsMapper inventoryWorkAssetsMapper = applicationContext.getBean(InventoryWorkAssetsMapper.class);
        ModelPropertySortMapper modelPropertySortMapper = applicationContext.getBean(ModelPropertySortMapper.class);

        ScanEngineService scanEngineService = applicationContext.getBean(ScanEngineService.class);
        RedisUtils redisUtils = applicationContext.getBean(RedisUtils.class);
        AssetsDiscoveryRunManage runManage = applicationContext.getBean(AssetsDiscoveryRunManage.class);
        AssetsInterfaceMapper assetsInterfaceMapper = applicationContext.getBean(AssetsInterfaceMapper.class);
        AssetsScanTotalResultMapper totalResultMapper = applicationContext.getBean(AssetsScanTotalResultMapper.class);
        modelServiceMap.put(InventoryWorkAssetsServiceImpl.ID, new InventoryWorkAssetsServiceImpl(inventoryWorkAssetsMapper, inventoryWorkInfoMapper, viewManager, this, mwUserApi));
        modelServiceMap.put(InventoryWorkInfoServiceImpl.ID, new InventoryWorkInfoServiceImpl(inventoryWorkInfoMapper, this, mwUserApi, viewManager, inventoryWorkAssetsMapper, messageSource));
        modelServiceMap.put(CustomModelServiceImpl.ID, new CustomModelServiceImpl(custModelMapper
                , modelManage, instanceInfoMapper, this, viewManager, tableHeaderAndDataManage, tableViewManageService));
        modelServiceMap.put(ModelPropertyServiceImpl.ID, new ModelPropertyServiceImpl(custModelMapper, this
                , modelPropertyCatologMapper, modelPropertySortMapper, viewManager));
        modelServiceMap.put(ModelTemplateServiceImpl.ID, new ModelTemplateServiceImpl(modelTemplateMapper, modelMacroMapper, this, mwTPServerService, zabbixDubboService));
        modelServiceMap.put(ModelVendorServiceImpl.ID, new ModelVendorServiceImpl(modelVendorMapper, this));

        InstanceGroupByPathAndStatusMapper instanceGroupByPathAndStatusMapper = applicationContext.getBean(InstanceGroupByPathAndStatusMapper.class);
        modelServiceMap.put(InstanceServiceImpl.ID
                , new InstanceServiceImpl(instanceInfoMapper, this, moduleIDManager, zabbixDubboService, listerList
                        ,scanEngineService, alertApiService, mwUserApi, assetsCodeCacheMapper, assetsInterfaceMapper, mwEngineManageService));

        modelServiceMap.put(ModelGroupServiceImpl.ID, new ModelGroupServiceImpl(modelGroupInfoMapper, custModelMapper, mwTPServerService, this, zabbixDubboService));
        modelServiceMap.put(ModelSpecificationServiceImpl.ID, new ModelSpecificationServiceImpl(modelSpecificationMapper, this,mwUserApi));
        modelServiceMap.put(ModelMacOuiServiceImpl.ID, new ModelMacOuiServiceImpl(modelMacOuiMapper, this));
        modelServiceMap.put(InstanceMonitorServiceImpl.ID, new InstanceMonitorServiceImpl(instanceMonitorMapper, this, moduleIDManager, zabbixDubboService,mwSyncEngineCommonService));
        modelServiceMap.put(InstanceDetailTabServiceImpl.ID, new InstanceDetailTabServiceImpl(instanceDetailTabMapper, this, moduleIDManager));
        modelServiceMap.put(LayoutItemMapperServiceImpl.ID, new LayoutItemMapperServiceImpl(layoutItemDataMapper, this, moduleIDManager));
        modelServiceMap.put(AssetsScanResultServiceImpl.ID, new AssetsScanResultServiceImpl(assetsScanResultMapper, this, moduleIDManager, assetScanService, mwUserApi, totalResultMapper));
        modelServiceMap.put(InstanceTableHeaderServiceImpl.ID, new InstanceTableHeaderServiceImpl(tableHeaderMapper, this));
        modelServiceMap.put(ItemNameMapperServiceImpl.ID, new ItemNameMapperServiceImpl(itemNameMapper, this, zabbixDubboService));
        modelServiceMap.put(CustomClassificationServiceImpl.ID, new CustomClassificationServiceImpl(customClassificationMapper, custModelMapper, mwTPServerService));
        modelServiceMap.put(InstanceViewImpl.ID, new InstanceViewImpl(instanceInfoMapper, this, custModelMapper, mwTPServerService, zabbixDubboService, mwUserApi, instanceImportHistoryService, messageSource, moduleIDManager, gridFsTemplate));
        modelServiceMap.put(UNumManageServiceImpl.ID, new UNumManageServiceImpl(uNumManageMapper, this));
        modelServiceMap.put(TableSearchConditionServiceImpl.ID, new TableSearchConditionServiceImpl(tableSearchConditionMapper));
        modelServiceMap.put(AssetsDiscoverServiceImpl.ID, new AssetsDiscoverServiceImpl(this, assetsDiscoverMapper
                , moduleIDManager, mwUserApi ,redisUtils ,assetsScanResultMapper ,runManage ,mwTPServerService,assetsScanResultMapper));
        modelServiceMap.put(MwCustomRuleServiceImpl.ID, new MwCustomRuleServiceImpl(this, mwCustomRuleMapper
                , moduleIDManager, mwUserApi ,redisUtils ,assetsScanResultMapper ,runManage ,mwTPServerService));

        modelServiceMap.put(AlgorithmInfoServiceImpl.ID, new AlgorithmInfoServiceImpl(this, algorithmInfoMapper, moduleIDManager, powerJobService));
        modelServiceMap.put(InstanceMetricServiceImpl.ID, new InstanceMetricServiceImpl(this, instanceMetricInfoMapper));
        modelServiceMap.put(MwCMDBConfigImpl.ID, new MwCMDBConfigImpl(this, mwCMDBConfigMapper));

        serviceParamConvertMap.put(CustomModelServiceImpl.class, new CustomServiceParamConverter());
        serviceParamConvertMap.put(ModelTemplateServiceImpl.class, new TemplateServiceParamConverter());
        serviceParamConvertMap.put(ModelVendorServiceImpl.class, new VendorServiceParamConverter());
        serviceParamConvertMap.put(InstanceServiceImpl.class, new InstanceServiceParamConverter());
        serviceParamConvertMap.put(ModelSpecificationServiceImpl.class, new SpecificationServiceParamConverter());
        serviceParamConvertMap.put(ModelMacOuiServiceImpl.class, new MacOuiServiceParamConverter());

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

}
